package se.lnu.axez.shaders;

import android.opengl.GLES20;
import android.util.Log;

/**
 * Base shader class for fragment and vertex shaders.
 * 
 * @author stan
 *
 */
public abstract class Shader {
	/**
	 * Indicates that shader requires {@link #ATTR_NAME_POSITION} attribute to be set.
	 * This attribute receives vertex coordinates.
	 */
	public static final int SHADER_FEATURE_ATTR_POSITION = 1;
	
	/**
	 * Indicates that shader requires {@link #ATTR_NAME_NORMAL} attribute to be set.
	 * This attribute receives vertex normal vector.
	 */
	public static final int SHADER_FEATURE_ATTR_NORMAL = 2;
	
	/**
	 * Indicates that shader requires {@link #ATTR_NAME_UV} attribute to be set.
	 * This attribute receives vertex texture coordinates.
	 */
	public static final int SHADER_FEATURE_ATTR_UV = 4;
	
	/**
	 * Indicates that shader requires {@link #UNI_NAME_MVP_MATRIX} uniform to be set.
	 */
	public static final int SHADER_FEATURE_UNI_MVP_MATRIX = 8;
	
	/**
	 * Indicates that shader requires {@link #UNI_NAME_MV_MATRIX} uniform to be set.
	 */
	public static final int SHADER_FEATURE_UNI_MV_MATRIX = 16;
	
	/**
	 * Indicates that shader requires {@link #UNI_NAME_NORMAL_MATRIX} uniform to be set.
	 */
	public static final int SHADER_FEATURE_UNI_NORMAL_MATRIX = 32;
	
	/**
	 * Shader attribute name for vertex coordinates.
	 */
	public static final String ATTR_NAME_POSITION = "a_vertex_coord";
	
	/**
	 * Shader attribute name for normal vector.
	 */
	public static final String ATTR_NAME_NORMAL = "a_normal";
	
	/**
	 * Shader attribute name for UV coordinates.
	 */
	public static final String ATTR_NAME_UV = "a_uv_coord";
	
	/**
	 * Shader uniform name for MVP matrix.
	 */
	public static final String UNI_NAME_MVP_MATRIX = "u_mvp_matrix";
	
	/**
	 * Shader uniform name for normal matrix.
	 */
	public static final String UNI_NAME_NORMAL_MATRIX = "u_normal_matrix";
	
	/**
	 * Shader uniform name for MV matrix.
	 */
	public static final String UNI_NAME_MV_MATRIX = "u_mv_matrix";
	

	
	/**
	 * Identifies vertex shader type.
	 */
	public static final int SHADER_TYPE_VERTEX = GLES20.GL_VERTEX_SHADER;

	/**
	 * Identifies fragment shader type.
	 */
	public static final int SHADER_TYPE_FRAGMENT = GLES20.GL_FRAGMENT_SHADER;
	
	/**
	 * Shader identifier indicating the shader was not yet compiled or compiled
	 * with error.
	 */
	public static final int INVALID_SHADER_ID = 0;
	
	/**
	 * LogCat tag.
	 */
	private static final String TAG = Shader.class.getSimpleName();
	
	private int shaderID = INVALID_SHADER_ID;

	/**
	 * This class can only be inherited within the package.
	 */
	Shader() {
	}
	
	/**
	 * Loads and compiles shader. This method must never be called by user code.
	 * It is always executed by the axeZ framework within OpenGL context.
	 */
	public void load() {
		this.shaderID = loadShader(this.getShaderCode(), this.getShaderType());
	}

	/**
	 * Gets shader identifier assigned by OpenGL.
	 * @return
	 */
	public int getShaderID() {
		return this.shaderID;
	}

	/**
	 * Implementation of this method must return string with shader program.
	 */
	protected abstract String getShaderCode();

	/**
	 * Implementation of this method must return shader type.
	 */
	protected abstract int getShaderType();
	
	protected abstract int getRequiredShaderFeatures();
	
	/**
	 * Loads shader to OpenGl and compiles it.
	 * 
	 * @return compiled shader identifier.
	 */
	private static int loadShader(String source, int shaderType) {
        int shader = GLES20.glCreateShader(shaderType);

        if (shader == 0) {
        	String msg = String.format("%s shader creation failed.", shaderType);
        	Log.e(TAG, msg);
        	throw new RuntimeException(msg);
        }
         
        GLES20.glShaderSource(shader, source);
        GLES20.glCompileShader(shader);

        // check if compilation succeeded
        int[] compiled = new int[1];
        GLES20.glGetShaderiv(shader, GLES20.GL_COMPILE_STATUS, compiled, 0);
        
        // if shader compiled successfully, return its ID
        if (compiled[0] == GLES20.GL_TRUE) {
        	return shader;
        }
        
        // otherwise report error and throw exception
    	String msg = String.format("Could not compile %s shader:", shaderType);
        Log.e(TAG, msg);
        String txt = GLES20.glGetShaderInfoLog(shader);
        Log.e(TAG, txt);
        GLES20.glDeleteShader(shader);
        throw new RuntimeException(msg);
	}
}
